package com.github.hgkmail.hello.interview.jvm.classloader;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//代理类$Proxy0.class就是最简单的字节码生成
//代理类有2种：1.有实现类 2.无实现类
//MyBatis是通过代理类实现，具体逻辑见MapperProxy（实现了InvocationHandler），通过代理类调用 jdbc connection来执行SQL（sql放在注解或xml文件）
//rpc可以通过代理类实现（比如dubbo）
//  rpc server 是有实现类的代理类，负责监听网络请求，然后反序列化参数，传给真正的实现类（一般是读写数据库）
//  rpc client 是没有实现类的代理类，序列化参数，然后发起网络请求，然后把响应封装成返回值
//todo 自己实现一个java rpc，server可以加上限流，client可以加上降级，server和client的地址和配置放在zookeeper
public class ProxyTest {

    //接口
    interface IHello {
        void sayHello();
    }

    //实现类
    static class Hello implements IHello {
        @Override
        public void sayHello() {
            System.out.println("Hello, world!");
        }
    }

    //代理类handler，这种需要一个实现类
    static class GenerateProxyWithImpl implements InvocationHandler {
        //实现类实例
        Object originObj;

        //生成代理类
        Object generate(Object ori) {
            originObj=ori;
            //字节码生成，会产生一个$Proxy0.class，具体拼装过程在sun.misc.ProxyGenerator
            return Proxy.newProxyInstance(originObj.getClass().getClassLoader(), originObj.getClass().getInterfaces(), this);
        }

        //调用代理类的任何方法，都会调用InvocationHandler的invoke()，这里可以加切面逻辑，比如打日志、网络传输等
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("welcome");
            Object res=method.invoke(originObj, args);
            System.out.println("exit");
            return res;
        }
    }

    //代理类handler，这种不需要实现类
    static class GenerateProxyWithoutImpl implements InvocationHandler {
        //生成代理类
        Object generate() {
            //字节码生成，会产生一个$Proxy0.class，具体拼装过程在sun.misc.ProxyGenerator
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{IHello.class}, this);
        }

        //调用代理类的任何方法，都会调用InvocationHandler的invoke()，这里可以加切面逻辑，比如打日志、网络传输等
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("welcome "+proxy.getClass().getName()+" "+method.getName());
            return null;
        }
    }

    public static void main(String[] args) {
        IHello hello = (IHello) new GenerateProxyWithImpl().generate(new Hello());
        hello.sayHello();

        IHello hello2 = (IHello) new GenerateProxyWithoutImpl().generate();
        hello2.sayHello();
    }
}
